package org.xqh.utils;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NoArgsConstructor;
import org.xqh.utils.encrypt.EncryptUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @ClassName XmlParseUtils
 * @Description TODO
 * @Author xuqianghui
 * @Date 2020/8/30 15:18
 * @Version 1.0
 */
@Slf4j
public class XmlParseUtils {

    public static void main(String[] args) throws IOException {
        testFormatObj();
    }

    public static void testParseXml() throws IOException {
        XmlMapper xmlMapper = new XmlMapper();
        String buffer = "PFRpbWVsaW5lT2JqZWN0PjxpZD4xMzQxMTY2OTA5MjI0OTM4MzA4MTwvaWQ+PHVzZXJuYW1lPnd4aWRfb25mcDAzd3d2ZDA2MjI8L3VzZXJuYW1lPjxjcmVhdGVUaW1lPjE1OTg3OTU1NDQ8L2NyZWF0ZVRpbWU+PGNvbnRlbnREZXNjPjwvY29udGVudERlc2M+PGNvbnRlbnREZXNjU2hvd1R5cGU+MDwvY29udGVudERlc2NTaG93VHlwZT48Y29udGVudERlc2NTY2VuZT4zPC9jb250ZW50RGVzY1NjZW5lPjxwcml2YXRlPjA8L3ByaXZhdGU+PHNpZ2h0Rm9sZGVkPjA8L3NpZ2h0Rm9sZGVkPjxzaG93RmxhZz4wPC9zaG93RmxhZz48YXBwSW5mbz48aWQ+PC9pZD48dmVyc2lvbj48L3ZlcnNpb24+PGFwcE5hbWU+PC9hcHBOYW1lPjxpbnN0YWxsVXJsPjwvaW5zdGFsbFVybD48ZnJvbVVybD48L2Zyb21Vcmw+PGlzRm9yY2VVcGRhdGU+MDwvaXNGb3JjZVVwZGF0ZT48L2FwcEluZm8+PHNvdXJjZVVzZXJOYW1lPjwvc291cmNlVXNlck5hbWU+PHNvdXJjZU5pY2tOYW1lPjwvc291cmNlTmlja05hbWU+PHN0YXRpc3RpY3NEYXRhPjwvc3RhdGlzdGljc0RhdGE+PHN0YXRFeHRTdHI+PC9zdGF0RXh0U3RyPjxDb250ZW50T2JqZWN0Pjxjb250ZW50U3R5bGU+MzwvY29udGVudFN0eWxlPjx0aXRsZT4mI3gwQTsmI3gwQTsmI3gwQTvkuaDov5HlubM8L3RpdGxlPjxkZXNjcmlwdGlvbj48L2Rlc2NyaXB0aW9uPjxtZWRpYUxpc3Q+PG1lZGlhPjxpZD4xMzQxMTY2OTA5MjU5NjY1ODMzMDwvaWQ+PHR5cGU+MjwvdHlwZT48dGl0bGU+5LuA5LmI5omN5piv55yf5q2j55qE5p625p6E6K6+6K6h77yfPC90aXRsZT48ZGVzY3JpcHRpb24+LSAgICAg5p625p6E55qE5a6a5LmJ5ZKM5p625p6E5pys6LSoICAgICAt5Zyo6L2v5Lu26KGM5Lia77yM5a+55LqO5LuA5LmI5piv5p625p6E77yM6YO95pyJ5b6I5aSa55qE5LqJ6K6677yM5q+P5Liq5Lq66YO95pyJ6Ieq5bex55qE55CG6KejPC9kZXNjcmlwdGlvbj48cHJpdmF0ZT4wPC9wcml2YXRlPjx1c2VyRGF0YT48L3VzZXJEYXRhPjxzdWJUeXBlPjA8L3N1YlR5cGU+PHZpZGVvU2l6ZSB3aWR0aD0iIiBoZWlnaHQ9IiI+PC92aWRlb1NpemU+PHVybCB0eXBlPSIxIiBtZDU9IiIgdmlkZW9tZDU9IiI+aHR0cDovL21wLndlaXhpbi5xcS5jb20vcz9fX2Jpej1Nekl6T0RJek56RTBOUT09bWlkPTI2NTQ0MjU1ODBpZHg9MnNuPTQ4ZWZlZTEwNWRmODc4ZmQyYjZlMTY3MThlNzU0MDc5Y2hrc209ZjJmZjk1NWFjNTg4MWM0YzQzYmYxNmRjMjBjYTlhMzg3NTY0ZTllOWE5Mjc4NDIyNDM2ZDc4OWZmZTc1ZTk2Yjk1ZjVlMDE4OWE1OW1wc2hhcmU9MXNjZW5lPTI0c3JjaWQ9MDgzMGh3MW84b2RYZndJNlMwU3ljMG84c2hhcmVyX3NoYXJldGltZT0xNTk4Nzk1MDc3MTc1c2hhcmVyX3NoYXJlaWQ9ZTE0MjQxODdmZTZlMzMzYjVlY2RkN2Q4NDE2OWVkZWYjcmQ8L3VybD48dGh1bWIgdHlwZT0iMSI+aHR0cHM6Ly9tbWJpei5xbG9nby5jbi9tbWJpel9qcGcvOVRQbjY2SFQ5MzFHbUJ0eGxVUUNpYUluMlBFcGF0a0lXT2hacEtRWjR3V1V0WENBSnp5V3g5TnZ4cGlhcGtsUmNiWTRaNWV5bHhNTW9VaG1WQnBudkpJdy8zMDA/d3hfZm10PWpwZWd3eGZyb209NDwvdGh1bWI+PHNpemUgd2lkdGg9IiIgaGVpZ2h0PSIiIHRvdGFsU2l6ZT0iIj48L3NpemU+PC9tZWRpYT48L21lZGlhTGlzdD48Y29udGVudFVybD48L2NvbnRlbnRVcmw+PC9Db250ZW50T2JqZWN0PjxhY3Rpb25JbmZvPjxhcHBNc2c+PG1lc3NhZ2VBY3Rpb24+PC9tZXNzYWdlQWN0aW9uPjwvYXBwTXNnPjwvYWN0aW9uSW5mbz48bG9jYXRpb24gcG9pQ2xhc3NpZnlJZD0iIiBwb2lOYW1lPSIiIHBvaUFkZHJlc3M9IiIgcG9pQ2xhc3NpZnlUeXBlPSIwIiBjaXR5PSIiPjwvbG9jYXRpb24+PHB1YmxpY1VzZXJOYW1lPjwvcHVibGljVXNlck5hbWU+PHN0cmVhbXZpZGVvPjxzdHJlYW12aWRlb3VybD48L3N0cmVhbXZpZGVvdXJsPjxzdHJlYW12aWRlb3RodW1idXJsPjwvc3RyZWFtdmlkZW90aHVtYnVybD48c3RyZWFtdmlkZW93ZWJ1cmw+PC9zdHJlYW12aWRlb3dlYnVybD48L3N0cmVhbXZpZGVvPjwvVGltZWxpbmVPYmplY3Q+";
        String xmlStr = EncryptUtils.base64Decode(buffer);
        System.out.println(xmlStr);
        //自动忽略无法对应pojo的字段
        xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        xmlStr = xmlStr.replaceAll("ContentObject", "contentObject");
        FriendCircle model = xmlMapper.readValue(xmlStr, FriendCircle.class);
        System.out.println(JSON.toJSONString(model));
    }

    public static void testFormatObj() {
        Test test = new Test();
        test.setName("abbb");
        test.setAge("12");
        List<Test> list = new ArrayList<>();
        list.add(Test.builder().name("11111").age("11").build());
        list.add(Test.builder().name("11112").age("11").build());
        list.add(Test.builder().name("11113").age("11").build());
        list.add(Test.builder().name("11114").age("11").build());
        System.out.println(objTransToXml(test, "msg"));
        System.out.println(formatXmlString(test, "", ""));
        System.out.println(convertToXml(test));
    }


    /**
     * 解析xml
     * @param xmlStr
     * @param clz
     * @param <T>
     * @return
     */
    public static <T> T parseXml(String xmlStr, Class<T> clz) {
        XmlMapper xmlMapper = new XmlMapper();
        //自动忽略无法对应pojo的字段
        xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        try {
            T model = xmlMapper.readValue(xmlStr, clz);
            return model;
        } catch (IOException e) {
            log.error("", e);
        }
        return null;
    }

    public static <T> String formatXmlString(T obj, String replaceStr, String replaceTo){
        XmlMapper xmlMapper = new XmlMapper();
        //自动忽略无法对应pojo的字段
        xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        try {
            String str = xmlMapper.writeValueAsString(obj);
            if(StringUtils.hasText(replaceStr) && StringUtils.hasText(replaceTo)){
                str = str.replaceAll(replaceStr, replaceTo);
            }
            return str;
        } catch (JsonProcessingException e) {
            log.error("", e);
        }
        return null;
    }

    /**
     * 将对象直接转换成String类型的 XML输出
     *
     * @param obj
     * @return
     */
    public static String convertToXml(Object obj) {
        // 创建输出流
        StringWriter sw = new StringWriter();
        try {
            // 利用jdk中自带的转换类实现
            JAXBContext context = JAXBContext.newInstance(obj.getClass());

            Marshaller marshaller = context.createMarshaller();
            // 格式化xml输出的格式
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
                    Boolean.TRUE);
            // 将对象转换成输出流形式的xml
            marshaller.marshal(obj, sw);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return sw.toString();
    }


    /**
     * 对象转 xml  目前仅支持一级 一个标签内 <msg name="" age="12" /> 这种格式
     * @param obj
     * @param tag
     * @param <T>
     * @return
     */
    public static <T> String objTransToXml(T obj, String tag){
        StringBuffer sb = new StringBuffer("<");
        sb.append(tag);
        Class clz = obj.getClass();
        Field[] fields = clz.getDeclaredFields();
        for (Field field : fields) {
            try {
                PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), obj.getClass());
                Method readMethod = descriptor.getReadMethod();
                Object o = readMethod.invoke(obj);
                sb.append(" ");
                sb.append(field.getName());
                sb.append("=");
                sb.append("\""+ (Objects.isNull(o) ? "" : o) +"\"");
            } catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        sb.append(" />");
        return sb.toString();
    }

    /**
     * 将对象根据路径转换成xml文件
     *
     * @param obj
     * @param path
     * @return
     */
    public static void convertToXml(Object obj, String path) {
        try {
            // 利用jdk中自带的转换类实现
            JAXBContext context = JAXBContext.newInstance(obj.getClass());

            Marshaller marshaller = context.createMarshaller();
            // 格式化xml输出的格式
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
                    Boolean.TRUE);
            // 将对象转换成输出流形式的xml
            // 创建输出流
            FileWriter fw = null;
            try {
                fw = new FileWriter(path);
            } catch (IOException e) {
                e.printStackTrace();
            }
            marshaller.marshal(obj, fw);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }

    @SuppressWarnings("unchecked")
    /**
     * 将String类型的xml转换成对象
     */
    public static Object convertXmlStrToObject(Class clazz, String xmlStr) {
        Object xmlObject = null;
        try {
            JAXBContext context = JAXBContext.newInstance(clazz);
            // 进行将Xml转成对象的核心接口
            Unmarshaller unmarshaller = context.createUnmarshaller();
            StringReader sr = new StringReader(xmlStr);
            xmlObject = unmarshaller.unmarshal(sr);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return xmlObject;
    }

    @SuppressWarnings("unchecked")
    /**
     * 将file类型的xml转换成对象
     */
    public static Object convertXmlFileToObject(Class clazz, String xmlPath) {
        Object xmlObject = null;
        try {
            JAXBContext context = JAXBContext.newInstance(clazz);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            FileReader fr = null;
            try {
                fr = new FileReader(xmlPath);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            xmlObject = unmarshaller.unmarshal(fr);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return xmlObject;
    }

    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @XmlRootElement(name = "Test")
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(propOrder = {"age","name","list","test"})
    public static class Test{
        private String name;
        private String age;
        private Integer test;
        private List<Test> list;
    }

    @Data
    public static class FriendCircle{
        private String id;
        private String username;
        private String createTime;
        private String contentDesc;
        private String sourceUserName;
        private String sourceNickName;
        private String statisticsData;

        private ContentObject contentObject;

        private WeappInfo weappInfo;
        private String canvasInfoXml;
        private List<ActionInfo> actionInfo;
        private String statExtStr;
        private String publicUserName;//公众号用户名
        private String location;

        private Streamvideo streamvideo;
        private AppInfo appInfo;

    }

    @Data
    public static class AppInfo{
        private String id;
    }

    @Data
    public static class ContentObject{

        private String contentStyle;
        private String contentSubStyle;
        private String title;
        private String description;
        private String contentUrl;
        private List<MediaItem> mediaList;
        private Mmreadershare mmreadershare;
    }

    @Data
    public static class Streamvideo{
        private String streamvideourl;
        private String streamvideothumburl;
        private String streamvideoweburl;
    }

    @Data
    public static class Mmreadershare{
        private String itemshowtype;
        private String ispaysubscribe;
    }

    @Data
    public static class ActionInfo{
        private String mediaTagName;
        private String messageExt;
        private String messageAction;
    }

    @Data
    public static class WeappInfo{

        private String appUserName;
        private String pagePath;
        private String version;
        private String debugMode;
        private String shareActionId;
        private String isGame;
        private String messageExtraData;
        private String subType;

    }

    @Data
    public static class MediaItem{

        private String id;
        private String type;
        private String title;
        private String description;
        private String url;
        private String thumb;
    }
}
